home *** CD-ROM | disk | FTP | other *** search
- # Source Generated with Decompyle++
- # File: in.pyc (Python 2.6)
-
- import gtk
- import cairo
- import pango
- import gobject
- import colors
- import math
- import random
- from simple_debug import simple_debug
- from gettext import gettext as _
- import sudoku
- TRACKER_COLORS = [ []([ x / 255 for x in cols ]) for cols in [
- (32, 74, 135),
- (78, 154, 6),
- (206, 92, 0),
- (143, 89, 2),
- (92, 53, 102),
- (85, 87, 83),
- (196, 160, 0)] ]
-
- def gtkcolor_to_rgb(c):
- return (c.red / float(65536), c.green / float(65536), c.blue / float(65536))
-
-
- def overlay(color_1, color_2, method = 1):
- return (color_1[0] + color_2[0] * method, color_1[1] + color_2[1] * method, color_1[2] + color_2[2] * method)
-
- ERROR_HIGHLIGHT_COLOR = (1, 0, 0)
- BASE_SIZE = 35
- BASE_FONT_SIZE = pango.SCALE * 13
- NOTE_FONT_SIZE = pango.SCALE * 6
- BORDER_WIDTH = 9
- BORDER_LINE_WIDTH = 4
- LITTLE_LINE_WIDTH = 0.25
- NORMAL_LINE_WIDTH = 1
- SPACING_FACTOR = 40
- SMALL_TO_BIG_FACTOR = 3.5
-
- class NumberSelector(gtk.EventBox):
- __gsignals__ = {
- 'changed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()) }
-
- def __init__(self, default = None, upper = 9):
- self.value = default
- gtk.EventBox.__init__(self)
- self.table = gtk.Table()
- self.add(self.table)
- side = int(math.sqrt(upper))
- n = 1
- for y in range(side):
- for x in range(side):
- b = gtk.Button()
- l = gtk.Label()
- if n == self.value:
- l.set_markup('<b><span size="x-small">%s</span></b>' % n)
- else:
- l.set_markup('<span size="x-small">%s</span>' % n)
- b.add(l)
- b.set_relief(gtk.RELIEF_HALF)
- l = b.get_children()[0]
- b.set_border_width(0)
- l.set_padding(0, 0)
- l.get_alignment()
- b.connect('clicked', self.number_clicked, n)
- self.table.attach(b, x, x + 1, y, y + 1)
- n += 1
-
-
- if self.value:
- db = gtk.Button()
- l = gtk.Label()
- l.set_markup_with_mnemonic('<span size="x-small">' + _('_Clear') + '</span>')
- db.add(l)
- l.show()
- db.connect('clicked', self.number_clicked, 0)
- self.table.attach(db, 0, side, y + 1, y + 2)
-
- self.connect('key-release-event', self.key_press_cb)
- self.show_all()
-
-
- def key_press_cb(self, w, e):
- txt = gtk.gdk.keyval_name(e.keyval)
- if txt == 'Escape':
- self.emit('changed')
- elif txt in ('0', 'Delete', 'BackSpace'):
- self.value = None
- self.emit('changed')
- else:
-
- try:
- self.value = int(txt)
- except:
- print "Can't make sense of %s" % txt
-
- self.emit('changed')
-
-
- def number_clicked(self, button, n):
- self.value = n
- self.emit('changed')
-
-
- def get_value(self):
- return self.value
-
-
- def set_value(self, n):
- self.value = n
-
-
-
- class NumberBox(gtk.Widget):
- text = ''
- top_note_text = ''
- bottom_note_text = ''
- read_only = False
- read_only_hidden = False
- _layout = None
- _top_note_layout = None
- _bottom_note_layout = None
- text_color = None
- highlight_color = None
- custom_background_color = None
- __gsignals__ = {
- 'value-about-to-change': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
- 'changed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
- 'undo-change': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
- 'notes-changed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()) }
- base_state = gtk.STATE_NORMAL
- number_picker_mode = False
- draw_boxes = False
-
- def __init__(self, upper = 9, text = ''):
- gtk.Widget.__init__(self)
- self.upper = upper
- self.font = self.style.font_desc
- self.font.set_size(BASE_FONT_SIZE)
- self.note_font = self.font.copy()
- self.note_font.set_size(NOTE_FONT_SIZE)
- self.set_property('can-focus', True)
- self.set_property('events', gtk.gdk.ALL_EVENTS_MASK)
- self.connect('button-press-event', self.button_press_cb)
- self.connect('key-release-event', self.key_press_cb)
- self.connect('enter-notify-event', self.pointer_enter_cb)
- self.connect('leave-notify-event', self.pointer_leave_cb)
- self.connect('focus-in-event', self.focus_in_cb)
- self.connect('focus-out-event', self.focus_out_cb)
- self.connect('motion-notify-event', self.motion_notify_cb)
- self.set_text(text)
-
-
- def pointer_enter_cb(self, *args):
- if not self.is_focus():
- self.set_state(gtk.STATE_PRELIGHT)
-
-
-
- def pointer_leave_cb(self, *args):
- self.set_state(self.base_state)
- self._toggle_box_drawing_(False)
-
-
- def focus_in_cb(self, *args):
- self.set_state(gtk.STATE_SELECTED)
- self.base_state = gtk.STATE_SELECTED
-
-
- def focus_out_cb(self, *args):
- self.set_state(gtk.STATE_NORMAL)
- self.base_state = gtk.STATE_NORMAL
- self.number_picker_mode = False
- if hasattr(self, 'npicker') and self.npicker:
- self.npicker.destroy()
- self.npicker = None
-
-
-
- def motion_notify_cb(self, *args):
- if self.is_focus():
- self._toggle_box_drawing_(True)
- else:
- self._toggle_box_drawing_(False)
-
-
- def _toggle_box_drawing_(self, val):
- if val and not (self.draw_boxes):
- self.draw_boxes = True
- self.queue_draw()
-
- if not val and self.draw_boxes:
- self.draw_boxes = False
- self.queue_draw()
-
-
-
- def button_press_cb(self, w, e):
- if self.read_only:
- return None
- if e.type == gtk.gdk._2BUTTON_PRESS:
- return None
- if self.is_focus():
- (x, y) = e.get_coords()
- alloc = self.get_allocation()
- my_w = alloc.width
- my_h = alloc.height
- if self.number_picker_mode:
- xperc = float(x) / my_w
- yperc = float(y) / my_h
- if xperc > 0.75:
- self.set_text_interactive('')
- self.number_picker_mode = False
- self.queue_draw()
- return None
- if xperc > 0.5:
- xval = 3
- elif xperc > 0.25:
- xval = 2
- else:
- xval = 1
- if yperc > 0.66:
- yval = 6
- elif yperc > 0.33:
- yval = 3
- else:
- yval = 0
- self.number_picker_mode = False
- self.set_text_interactive('')
- self.set_text_interactive(str(xval + yval))
- else:
- border_height = float(BORDER_WIDTH) / BASE_SIZE
- if float(y) / my_h < border_height:
- self.show_note_editor(top = True)
- elif float(y) / my_h > 1 - border_height:
- self.show_note_editor(top = False)
- elif hasattr(self, 'npicker') and self.npicker:
- self.npicker.destroy()
- self.npicker = None
-
- self.show_number_picker()
- else:
- self.grab_focus()
-
-
- def key_press_cb(self, w, e):
- if self.read_only:
- return None
- txt = gtk.gdk.keyval_name(e.keyval)
- if type(txt) == type(None):
- return None
- txt = txt.replace('KP_', '')
- if self.get_text() == txt:
- return None
- if txt in ('0', 'Delete', 'BackSpace'):
- self.set_text_interactive('')
- elif txt in ('n', 'N'):
- self.show_note_editor(top = True)
- elif txt in ('m', 'M'):
- self.show_note_editor(top = False)
- elif [] in [ str(n) for n in range(1, self.upper + 1) ]:
- self.set_text_interactive(txt)
-
-
-
- def note_changed_cb(self, w, top = False):
- if top:
- self.set_note_text_interactive(top_text = w.get_text())
- else:
- self.set_note_text_interactive(bottom_text = w.get_text())
-
-
- def show_note_editor(self, top = True):
- alloc = self.get_allocation()
- w = gtk.Window()
- w.set_property('skip-pager-hint', True)
- w.set_property('skip-taskbar-hint', True)
- w.set_decorated(False)
- w.set_position(gtk.WIN_POS_MOUSE)
- w.set_size_request(alloc.width, alloc.height / 2)
- f = gtk.Frame()
- e = gtk.Entry()
- f.add(e)
- if top:
- e.set_text(self.top_note_text)
- else:
- e.set_text(self.bottom_note_text)
- w.add(f)
- e.connect('changed', self.note_changed_cb, top)
- e.connect('focus-out-event', (lambda e, ev, w: w.destroy()), w)
- e.connect('activate', (lambda e, w: w.destroy()), w)
- (x, y) = self.window.get_origin()
- if top:
- w.move(x, y)
- else:
- w.move(x, y + int(alloc.height * 0.6))
- w.show_all()
- e.grab_focus()
-
-
- def number_changed_cb(self, ns, w):
- w.destroy()
- self.set_text_interactive('')
- newval = ns.get_value()
- if newval:
- self.set_text_interactive(str(newval))
-
-
-
- def show_number_picker(self):
- w = gtk.Window(type = gtk.WINDOW_POPUP)
- ns = NumberSelector(upper = self.upper, default = self.get_value())
- ns.connect('changed', self.number_changed_cb, w)
- w.grab_focus()
- w.add(ns)
- r = w.get_allocation()
- my_origin = self.window.get_origin()
- (x, y) = self.window.get_size()
- (popupx, popupy) = w.get_size()
- overlapx = popupx - x
- overlapy = popupy - y
- w.move(my_origin[0] - overlapx / 2, my_origin[1] - overlapy / 2)
- w.show()
- self.npicker = w
-
-
- def set_text_interactive(self, text):
- self.emit('value-about-to-change')
- self.set_text(text)
- self.queue_draw()
- self.emit('changed')
-
-
- def set_font(self, font):
- if type(font) == str:
- font = pango.FontDescription(font)
-
- self.font = font
- if self.text:
- self.set_text(self.text)
-
- self.queue_draw()
-
-
- def set_note_font(self, font):
- if type(font) == str:
- font = pango.FontDescription(font)
-
- self.note_font = font
- if self.top_note_text or self.bottom_note_text:
- self.set_note_text(self.top_note_text, self.bottom_note_text)
-
- self.queue_draw()
-
-
- def set_text(self, text):
- self.text = text
- self._layout = self.create_pango_layout(text)
- self._layout.set_font_description(self.font)
-
-
- def set_notes(self, notes):
- '''Hackish method to allow easy use of Undo API.
-
- Undo API requires a set method that is called with one
- argument (the result of a get method)'''
- self.set_note_text(top_text = notes[0], bottom_text = notes[1])
- self.queue_draw()
-
-
- def set_note_text(self, top_text = None, bottom_text = None):
- if top_text is not None:
- self.top_note_text = top_text
- self._top_note_layout = self.create_pango_layout(top_text)
- self._top_note_layout.set_font_description(self.note_font)
-
- if bottom_text is not None:
- self.bottom_note_text = bottom_text
- self._bottom_note_layout = self.create_pango_layout(bottom_text)
- self._bottom_note_layout.set_font_description(self.note_font)
-
- self.queue_draw()
-
-
- def set_note_text_interactive(self, *args, **kwargs):
- self.emit('value-about-to-change')
- self.set_note_text(*args, **kwargs)
- self.emit('notes-changed')
-
-
- def do_realize(self):
- self.set_flags(self.flags() | gtk.REALIZED)
- self.window = gtk.gdk.Window(self.get_parent_window(), width = self.allocation.width, height = self.allocation.height, window_type = gtk.gdk.WINDOW_CHILD, wclass = gtk.gdk.INPUT_OUTPUT, event_mask = self.get_events() | gtk.gdk.EXPOSURE_MASK)
- self.window.set_user_data(self)
- self.style.attach(self.window)
- self.style.set_background(self.window, gtk.STATE_NORMAL)
- self.window.move_resize(*self.allocation)
-
-
- def do_unrealize(self):
- self.window.set_user_data(None)
-
-
- def do_size_request(self, requisition):
- (width, height) = self._layout.get_size()
- if width > height:
- side = width / pango.SCALE
- else:
- side = height / pango.SCALE
- requisition.width = side
- requisition.height = side
-
-
- def do_size_allocate(self, allocation):
- self.allocation = allocation
- if self.flags() & gtk.REALIZED:
- self.window.move_resize(*allocation)
-
-
-
- def do_expose_event(self, event):
- (x, y, w, h) = self.allocation
- cr = self.window.cairo_create()
- if h < w:
- scale = h / float(BASE_SIZE)
- else:
- scale = w / float(BASE_SIZE)
- cr.scale(scale, scale)
- self.draw_background_color(cr)
- if self.number_picker_mode:
- self.draw_numbers(cr)
- return None
- if self.is_focus():
- self.draw_highlight_box(cr)
-
- self.draw_normal_box(cr)
- self.draw_text(cr)
- if self.draw_boxes and self.is_focus():
- self.draw_note_area_highlight_box(cr)
-
-
-
- def draw_background_color(self, cr):
- if self.read_only:
- if self.custom_background_color:
- (r, g, b) = self.custom_background_color
- cr.set_source_rgb(r * 0.6, g * 0.6, b * 0.6)
- else:
- cr.set_source_color(self.style.base[gtk.STATE_INSENSITIVE])
- elif self.is_focus():
- cr.set_source_color(self.style.base[gtk.STATE_SELECTED])
- elif self.custom_background_color:
- cr.set_source_rgb(*self.custom_background_color)
- else:
- cr.set_source_color(self.style.base[self.state])
- cr.rectangle(0, 0, BASE_SIZE, BASE_SIZE)
- cr.fill()
-
-
- def draw_normal_box(self, cr):
- state = self.state
- if state == gtk.STATE_SELECTED:
- state = gtk.STATE_NORMAL
-
- cr.set_source_color(self.style.mid[state])
- cr.rectangle(NORMAL_LINE_WIDTH * 0.5, NORMAL_LINE_WIDTH * 0.5, BASE_SIZE - NORMAL_LINE_WIDTH, BASE_SIZE - NORMAL_LINE_WIDTH)
- cr.set_line_width(NORMAL_LINE_WIDTH)
- cr.set_line_join(cairo.LINE_JOIN_ROUND)
- cr.stroke()
- cr.set_source_color(self.style.dark[state])
- cr.rectangle(NORMAL_LINE_WIDTH * 0.25, NORMAL_LINE_WIDTH * 0.25, BASE_SIZE - NORMAL_LINE_WIDTH * 0.5, BASE_SIZE - NORMAL_LINE_WIDTH * 0.5)
- cr.set_line_width(NORMAL_LINE_WIDTH * 0.5)
- cr.set_line_join(cairo.LINE_JOIN_MITER)
- cr.stroke()
-
-
- def draw_highlight_box(self, cr):
- cr.set_source_color(self.style.base[gtk.STATE_SELECTED])
- cr.rectangle(BORDER_LINE_WIDTH * 0.5, BORDER_LINE_WIDTH * 0.5, BASE_SIZE - BORDER_LINE_WIDTH, BASE_SIZE - BORDER_LINE_WIDTH)
- cr.set_line_width(BORDER_LINE_WIDTH)
- cr.set_line_join(cairo.LINE_JOIN_ROUND)
- cr.stroke()
-
-
- def draw_note_area_highlight_box(self, cr):
- cr.set_source_color(self.style.mid[self.state])
- cr.set_line_width(NORMAL_LINE_WIDTH)
- cr.set_line_join(cairo.LINE_JOIN_ROUND)
- cr.rectangle(NORMAL_LINE_WIDTH * 0.5, NORMAL_LINE_WIDTH * 0.5, BASE_SIZE - NORMAL_LINE_WIDTH, BORDER_WIDTH - NORMAL_LINE_WIDTH)
- cr.stroke()
- cr.rectangle(NORMAL_LINE_WIDTH * 0.5, BASE_SIZE - BORDER_WIDTH - NORMAL_LINE_WIDTH * 0.5, BASE_SIZE - NORMAL_LINE_WIDTH, BASE_SIZE - NORMAL_LINE_WIDTH)
- cr.stroke()
-
-
- def draw_text(self, cr):
- if self.text_color:
- cr.set_source_rgb(*self.text_color)
- elif self.read_only:
- cr.set_source_color(self.style.text[gtk.STATE_NORMAL])
- else:
- cr.set_source_color(self.style.text[self.state])
- if self._layout:
- (fontw, fonth) = self._layout.get_pixel_size()
- cr.move_to(BASE_SIZE / 2 - fontw / 2, BASE_SIZE / 2 - fonth / 2)
- cr.update_layout(self._layout)
- cr.show_layout(self._layout)
-
- cr.set_source_color(self.style.text[self.state])
- if self._top_note_layout:
- (fontw, fonth) = self._top_note_layout.get_pixel_size()
- cr.move_to(NORMAL_LINE_WIDTH, 0)
- cr.update_layout(self._top_note_layout)
- cr.show_layout(self._top_note_layout)
-
- if self._bottom_note_layout:
- (fontw, fonth) = self._bottom_note_layout.get_pixel_size()
- cr.move_to(NORMAL_LINE_WIDTH, BASE_SIZE - fonth)
- cr.update_layout(self._bottom_note_layout)
- cr.show_layout(self._bottom_note_layout)
-
-
-
- def draw_numbers(self, cr):
- if not hasattr(self, 'number_text'):
- self.small_digit_height = 1
- self.small_digit_width = 1
- self.number_text = []
- for n in range(self.upper):
- if type(n) == int:
- n = str(n + 1)
-
- txt = self.create_pango_layout(n)
- txt.set_font_description(self.note_font)
- if not hasattr(self, 'bold_note_font'):
- self.bold_note_font = self.note_font.copy()
- self.bold_note_font.set_weight(pango.WEIGHT_BOLD)
-
- bold_txt = self.create_pango_layout(n)
- bold_txt.set_font_description(self.bold_note_font)
- self.number_text.append((txt, bold_txt))
- (w, h) = bold_txt.get_pixel_size()
- if w > self.small_digit_width:
- self.small_digit_width = w * 1.2
-
- if h > self.small_digit_height:
- self.small_digit_height = h
- continue
-
-
- val = self.get_value()
- cols = (BASE_SIZE - NORMAL_LINE_WIDTH * 2) / self.small_digit_width
- rows = (BASE_SIZE - NORMAL_LINE_WIDTH * 2) / self.small_digit_height
- cols = 3
- rows = 3
- row_size = BASE_SIZE / rows
- col_size = BASE_SIZE / cols
- n = 0
- for y in range(rows):
- for x in range(cols):
- if y < 2 and x > 2:
- continue
-
- if n >= len(self.number_text):
- break
-
- txt = self.number_text[n]
- if val == n + 1:
- layout = txt[1]
- else:
- layout = txt[0]
- (w, h) = layout.get_pixel_size()
- xpadding = (col_size - w) / 2
- ypadding = (row_size - h) / 2
- cr.set_source_color(gtk.gdk.Color(65536, 0, 0))
- cr.set_line_width(LITTLE_LINE_WIDTH)
- cr.rectangle(LITTLE_LINE_WIDTH * 0.5 + NORMAL_LINE_WIDTH * 0.5 + x * col_size, LITTLE_LINE_WIDTH * 0.5 + NORMAL_LINE_WIDTH * 0.5 + y * row_size, LITTLE_LINE_WIDTH * 0.5 + NORMAL_LINE_WIDTH * 0.5 + (x + 1) * col_size, LITTLE_LINE_WIDTH * 0.5 + NORMAL_LINE_WIDTH * 0.5 + (y + 1) * row_size)
- cr.stroke()
- cr.set_source_color(self.style.text[self.state])
- cr.move_to(NORMAL_LINE_WIDTH + x * col_size + xpadding, NORMAL_LINE_WIDTH + y * row_size + ypadding)
- cr.update_layout(layout)
- cr.show_layout(layout)
- n += 1
-
-
-
-
- def set_text_color(self, color):
- self.text_color = color
- self.queue_draw()
-
-
- def set_background_color(self, color):
- self.custom_background_color = color
- self.queue_draw()
-
-
- def hide_notes(self):
- pass
-
-
- def show_notes(self):
- pass
-
-
- def set_value_from_undo(self, v):
- self.set_value(v)
- self.emit('undo_change')
-
-
- def set_value(self, v):
- if v < v:
- pass
- elif v <= self.upper:
- self.set_text(str(v))
- else:
- self.set_text('')
- self.queue_draw()
-
-
- def get_value(self):
-
- try:
- return int(self.text)
- except:
- return None
-
-
-
- def get_text(self):
- return self.text
-
-
- def get_note_text(self):
- return (self.top_note_text, self.bottom_note_text)
-
-
-
- class SudokuNumberBox(NumberBox):
- normal_color = None
- highlight_color = ERROR_HIGHLIGHT_COLOR
-
- def set_color(self, color):
- self.normal_color = color
- self.set_text_color(self.normal_color)
-
-
- def unset_color(self):
- self.set_color(None)
-
-
- def set_error_highlight(self, val):
- if val:
- self.set_text_color((1, 0, 0))
- else:
- self.set_text_color(self.normal_color)
-
-
- def set_read_only(self, val):
- self.read_only = val
- if not hasattr(self, 'bold_font'):
- self.normal_font = self.font
- self.bold_font = self.font.copy()
- self.bold_font.set_weight(pango.WEIGHT_BOLD)
-
- if self.read_only:
- self.set_font(self.bold_font)
- else:
- self.set_font(self.normal_font)
- self.queue_draw()
-
-
- def set_impossible(self, val):
- if val:
- self.set_text('X')
- else:
- self.set_text('')
-
-
- gobject.type_register(NumberBox)
-
- class SudokuNumberGrid(gtk.AspectFrame):
-
- def __init__(self, group_size = 9):
- self.table = gtk.Table(rows = group_size, columns = group_size, homogeneous = True)
- self.group_size = group_size
- self.__entries__ = { }
- for x in range(self.group_size):
- for y in range(self.group_size):
- e = SudokuNumberBox(upper = self.group_size)
- e.x = x
- e.y = y
- self.table.attach(e, x, x + 1, y, y + 1)
- self.__entries__[(x, y)] = e
-
-
- gtk.AspectFrame.__init__(self, obey_child = False)
- self.set_shadow_type(gtk.SHADOW_NONE)
- self.eb = gtk.EventBox()
- self.eb.add(self.table)
- self.add(self.eb)
- self.connect('size-allocate', self.allocate_cb)
- self.show_all()
-
-
- def allocate_cb(self, w, rect):
- if rect.width > rect.height:
- side = rect.height
- else:
- side = rect.width
- spacing = float(side) / (self.group_size * SPACING_FACTOR)
- if spacing == 0:
- spacing = 1
-
- if hasattr(self, 'small_spacing') and spacing == self.small_spacing:
- return None
- self.change_spacing(spacing)
-
-
- def change_spacing(self, small_spacing):
- self.small_spacing = small_spacing
- self.big_spacing = int(small_spacing * SMALL_TO_BIG_FACTOR)
- self.table.set_row_spacings(int(small_spacing))
- self.table.set_col_spacings(int(small_spacing))
- box_side = int(math.sqrt(self.group_size))
- for n in range(1, box_side):
- self.table.set_row_spacing(box_side * n - 1, self.big_spacing)
- self.table.set_col_spacing(box_side * n - 1, self.big_spacing)
-
- self.table.set_border_width(self.big_spacing)
-
-
- def get_focused_entry(self):
- for e in self.__entries__.values():
- if e.is_focus():
- return e
-
-
-
- def set_bg_color(self, color):
- if type(color) == str:
-
- try:
- color = gtk.gdk.color_parse(color)
- print 'set_bg_color handed Bad color', color
- return None
-
-
- self.eb.modify_bg(gtk.STATE_NORMAL, color)
- self.eb.modify_base(gtk.STATE_NORMAL, color)
- self.eb.modify_fg(gtk.STATE_NORMAL, color)
- self.table.modify_bg(gtk.STATE_NORMAL, color)
- self.table.modify_base(gtk.STATE_NORMAL, color)
- self.table.modify_fg(gtk.STATE_NORMAL, color)
- for e in self.__entries__.values():
- e.modify_bg(gtk.STATE_NORMAL, color)
-
-
-
-
- class ParallelDict(dict):
- '''A handy new sort of dictionary for tracking conflicts.
-
- pd = ParallelDict()
- pd[1] = [2,3,4] # 1 is linked with 2,3 and 4
- pd -> {1:[2,3,4],2:[1],3:[1],4:[1]}
- pd[2] = [1,3,4] # 2 is linked with 3 and 4 as well as 1
- pd -> {1: [2,3,4],2:[3,4],3:[1,2],4:[1,2]}
- Now for the cool part...
- del pd[1]
- pd -> {2: [2,3],3:[2],4:[2]}
-
- Pretty neat, no?
- '''
-
- def __init__(self, *args):
- dict.__init__(self, *args)
-
-
- def __setitem__(self, k, v):
- dict.__setitem__(self, k, set(v))
- for i in v:
- if i == k:
- continue
-
- if self.has_key(i):
- self[i].add(k)
- continue
- dict.__setitem__(self, i, set([
- k]))
-
-
-
- def __delitem__(self, k):
- v = self[k]
- dict.__delitem__(self, k)
- for i in v:
- if i == k:
- continue
-
- if self.has_key(i):
- if k in self[i]:
- self[i].remove(k)
-
- if not self[i]:
- dict.__delitem__(self, i)
-
- self[i]
-
-
-
-
- class SudokuGameDisplay(SudokuNumberGrid, gobject.GObject):
- __gsignals__ = {
- 'focus-changed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
- 'puzzle-finished': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()) }
- do_highlight_cells = False
-
- def __init__(self, grid = None, group_size = 9, show_impossible_implications = False):
- group_size = int(group_size)
- self.hints = 0
- self.always_show_hints = False
- self.auto_fills = 0
- self.show_impossible_implications = show_impossible_implications
- self.impossible_hints = 0
- self.impossibilities = []
- self.trackers = { }
- self.__trackers_tracking__ = { }
- self.__colors_used__ = [
- None,
- ERROR_HIGHLIGHT_COLOR]
- gobject.GObject.__init__(self)
- SudokuNumberGrid.__init__(self, group_size = group_size)
- self.setup_grid(grid, group_size)
- for e in self.__entries__.values():
- e.show()
- e.connect('undo-change', self.entry_callback)
- e.connect('changed', self.entry_callback)
- e.connect('focus-in-event', self.focus_callback)
-
- self.connect('focus-changed', self.highlight_cells)
-
- __init__ = simple_debug(__init__)
-
- def focus_callback(self, e, event):
- self.focused = e
- self.emit('focus-changed')
-
- focus_callback = simple_debug(focus_callback)
-
- def get_highlight_colors(self):
- entry = self.__entries__.values()[0]
- default_color = gtkcolor_to_rgb(entry.style.bg[gtk.STATE_SELECTED])
- hsv = colors.rgb_to_hsv(*default_color)
- box_s = hsv[1]
- box_v = hsv[2]
- if box_v < 0.5:
- box_v = box_v * 2
-
- if box_s > 0.75:
- box_s = box_s * 0.5
- else:
- box_s = box_s * 1.5
- if box_s > 1:
- box_s = 1
-
- self.box_color = colors.hsv_to_rgb(hsv[0], box_s, box_v)
- self.box_and_row_color = colors.rotate_hue_rgb(*self.box_color, **{
- 'rotate_by': 0.33 / 2 })
- self.row_color = colors.rotate_hue_rgb(*self.box_color, **{
- 'rotate_by': 0.33 })
- self.col_color = colors.rotate_hue_rgb(*self.box_color, **{
- 'rotate_by': 0.66 })
- self.box_and_col_color = colors.rotate_hue_rgb(*self.box_color, **{
- 'rotate_by': 1 - 0.33 / 2 })
-
-
- def toggle_highlight(self, val):
- self.do_highlight_cells = val
- self.unhighlight_cells()
- if hasattr(self, 'focused') and self.focused:
- self.highlight_cells()
-
-
-
- def unhighlight_cells(self, *args):
- for e in self.__entries__.values():
- e.set_background_color(None)
-
-
-
- def highlight_cells(self, *args):
- if not self.do_highlight_cells:
- return None
- self.unhighlight_cells()
- if not hasattr(self, 'box_color'):
- self.get_highlight_colors()
-
- my_x = self.focused.x
- my_y = self.focused.y
- if not hasattr(self.grid, 'col_coords'):
- return None
- for x, y in self.grid.col_coords[my_x]:
- if (x, y) != (my_x, my_y):
- self.__entries__[(x, y)].set_background_color(self.col_color)
- continue
- hasattr(self.grid, 'col_coords')
-
- for x, y in self.grid.row_coords[my_y]:
- if (x, y) != (my_x, my_y):
- self.__entries__[(x, y)].set_background_color(self.row_color)
- continue
-
- for x, y in self.grid.box_coords[self.grid.box_by_coords[(my_x, my_y)]]:
- if (x, y) != (my_x, my_y):
- e = self.__entries__[(x, y)]
- if x == my_x:
- e.set_background_color(self.box_and_col_color)
- elif y == my_y:
- e.set_background_color(self.box_and_row_color)
- else:
- e.set_background_color(self.box_color)
- x == my_x
-
-
-
- def show_hint(self):
- if hasattr(self, 'focused'):
- entry = self.focused
- if entry.read_only or entry.get_text():
- pass
- else:
- self.show_hint_for_entry(entry, interactive = True)
-
-
- show_hint = simple_debug(show_hint)
-
- def show_hint_for_entry(self, entry, interactive = False):
- if interactive:
- set_method = entry.set_note_text_interactive
- else:
- set_method = entry.set_note_text
- vals = self.grid.possible_values(entry.x, entry.y)
- vals = list(vals)
- vals.sort()
- if vals:
- []([ str(v) for v in vals ])
- txt = []([ str(v) for v in vals ])
- if txt != entry.get_text():
- set_method(bottom_text = txt)
- self.hints += 1
-
- elif not entry.get_text():
- if entry.get_text() != 'X':
- self.hints += 1
- set_method(bottom_text = 'X')
-
- else:
- set_method(bottom_text = '')
-
-
- def num_to_str(self, n):
- if n >= 10:
- return SuperNumberEntry.conversions[n]
- return str(n)
-
- num_to_str = simple_debug(num_to_str)
-
- def reset_grid(self):
- '''Reset grid to its original setup.
-
- Return a list of items we removed so that callers can handle
- e.g. Undo properly'''
- removed = []
- for x in range(self.group_size):
- for y in range(self.group_size):
- if not self.grid.virgin._get_(x, y):
- val = self.__entries__[(x, y)].get_value()
- if val:
- removed.append((x, y, val, self.trackers_for_point(x, y, val)))
- self.remove(x, y, do_removal = True)
-
- val
-
-
- return removed
-
- reset_grid = simple_debug(reset_grid)
-
- def clear_notes(self, clear_args = {
- 'top_text': '',
- 'bottom_text': '' }):
- '''Remove all notes.'''
- self.removed = []
- for x in range(self.group_size):
- for y in range(self.group_size):
- e = self.__entries__[(x, y)]
- (top, bottom) = e.get_note_text()
- if top or bottom:
- self.removed.append((x, y, (top, bottom)))
- e.set_note_text(**clear_args)
- e.queue_draw()
- continue
-
-
- return self.removed
-
-
- def clear_hints(self):
- self.clear_notes(clear_args = {
- 'bottom_text': '' })
-
-
- def blank_grid(self):
- for x in range(self.group_size):
- for y in range(self.group_size):
- self.remove(x, y)
- e = self.__entries__[(x, y)]
- e.set_read_only(False)
-
-
- self.grid = None
- self.clear_notes()
-
- blank_grid = simple_debug(blank_grid)
-
- def change_grid(self, grid, group_size):
- self.auto_fills = 0
- self.hints = 0
- self.impossible_hints = 0
- self.trackers = { }
- self.__trackers_tracking__ = { }
- self.__colors_used__ = [
- None,
- ERROR_HIGHLIGHT_COLOR]
- self.blank_grid()
- self.setup_grid(grid, group_size)
-
- change_grid = simple_debug(change_grid)
-
- def load_game(self, game):
- '''Load a game.
-
- A game is simply a two lined string where the first line represents our
- virgin self and line two represents our game-in-progress.
- '''
- self.blank_grid()
- if '\n' in game:
- (virgin, in_prog) = game.split('\n')
- else:
- virgin = game
- in_prog = ''
- group_size = int(math.sqrt(len(virgin.split())))
- self.change_grid(virgin, group_size = group_size)
- if in_prog:
- values = [ int(c) for c in in_prog.split() ]
- for row in range(group_size):
- for col in range(group_size):
- index = row * 9 + col
- if values[index] and not self.grid._get_(col, row):
- self.add_value_to_ui(col, row, values[index])
- continue
- []
-
-
-
-
- load_game = simple_debug(load_game)
-
- def setup_grid(self, grid, group_size):
- self.doing_initial_setup = True
- self.__error_pairs__ = ParallelDict()
- if isinstance(grid, sudoku.SudokuGrid):
- self.grid = sudoku.InteractiveSudoku(grid.grid, group_size = grid.group_size)
- else:
- self.grid = sudoku.InteractiveSudoku(grid, group_size = group_size)
- for x in range(group_size):
- for y in range(group_size):
- val = self.grid._get_(x, y)
- if val:
- self.add_value(x, y, val)
- continue
-
-
- self.doing_initial_setup = False
-
- setup_grid = simple_debug(setup_grid)
-
- def entry_callback(self, widget, *args):
- if not widget.get_text():
- if self.grid and self.grid._get_(widget.x, widget.y):
- self.grid.remove(widget.x, widget.y)
-
- self.remove(widget.x, widget.y)
- else:
- self.entry_validate(widget)
- if self.show_impossible_implications:
- self.mark_impossible_implications(widget.x, widget.y)
-
- if self.always_show_hints:
- self.update_all_hints()
-
-
- entry_callback = simple_debug(entry_callback)
-
- def update_all_hints(self):
- for x in range(self.group_size):
- for y in range(self.group_size):
- e = self.__entries__[(x, y)]
- if e.read_only:
- continue
- if e.get_text():
- e.set_note_text(bottom_text = '')
- continue
- self.show_hint_for_entry(e)
-
-
-
-
- def entry_validate(self, widget, *args):
- val = widget.get_value()
-
- try:
- self.add_value(widget.x, widget.y, val)
- if self.grid.check_for_completeness():
- self.emit('puzzle-finished')
- except sudoku.ConflictError:
- err = None
- conflicts = self.grid.find_conflicts(err.x, err.y, err.value)
- for conflict in conflicts:
- widget.set_error_highlight(True)
- self.__entries__[conflict].set_error_highlight(True)
-
- self.__error_pairs__[(err.x, err.y)] = conflicts
-
-
- entry_validate = simple_debug(entry_validate)
-
- def add_value_to_ui(self, x, y, val, trackers = []):
- '''Add our value back to our grid come hell or high water.
-
- We add our value -- if there is an error, we make the value
- appear as if the user had typed it; i.e. it will show up with
- error highlighting.'''
-
- try:
- self.add_value(x, y, val, trackers = [])
- except sudoku.ConflictError:
- self.entry_validate(self.__entries__[(x, y)])
-
-
-
- def add_value(self, x, y, val, trackers = []):
- '''Add value val at position x,y.
-
- If tracker is True, we track it with tracker ID tracker.
-
- Otherwise, we use any currently tracking trackers to track our addition.
-
- Providing the tracker arg is mostly useful for e.g. undo/redo
- or removed items.
-
- To specify NO trackers, use trackers=[-1]
- '''
- self.__entries__[(x, y)].set_value(val)
- if self.doing_initial_setup:
- self.__entries__[(x, y)].set_read_only(True)
-
- if trackers:
- for tracker in trackers:
- if tracker == -1:
- pass
-
- self.__entries__[(x, y)].set_color(self.get_tracker_color(tracker))
- self.trackers[tracker].append((x, y, val))
-
- elif True in self.__trackers_tracking__.values():
- for k, v in self.__trackers_tracking__.items():
- if v:
- self.__entries__[(x, y)].set_color(self.get_tracker_color(k))
- self.trackers[k].append((x, y, val))
- continue
-
-
- self.grid.add(x, y, val, True)
- self.__entries__[(x, y)].queue_draw()
-
- add_value = simple_debug(add_value)
-
- def remove(self, x, y, do_removal = False):
- '''Remove x,y from our visible grid.
-
- If do_removal, remove it from our underlying grid as well.
- '''
- e = self.__entries__[(x, y)]
- if do_removal and self.grid and self.grid._get_(x, y):
- self.grid.remove(x, y)
-
- if self.__error_pairs__.has_key((x, y)):
- e.set_error_highlight(False)
- errors_removed = self.__error_pairs__[(x, y)]
- del self.__error_pairs__[(x, y)]
- for coord in errors_removed:
- if not self.__error_pairs__.has_key(coord):
- linked_entry = self.__entries__[coord]
- linked_entry.set_error_highlight(False)
- if self.grid and not self.grid._get_(linked_entry.x, linked_entry.y):
- self.entry_validate(linked_entry)
-
- not self.grid._get_(linked_entry.x, linked_entry.y)
-
-
- for t in self.trackers_for_point(x, y):
- remove = []
- for crumb in self.trackers[t]:
- if crumb[0] == x and crumb[1] == y:
- remove.append(crumb)
- continue
-
- for r in remove:
- self.trackers[t].remove(r)
-
-
- if e.get_text():
- e.set_value(0)
-
- e.unset_color()
-
- remove = simple_debug(remove)
-
- def auto_fill(self):
- changed = self.grid.auto_fill()
- retval = []
- for coords, val in changed:
- self.add_value(coords[0], coords[1], val)
- retval.append((coords[0], coords[1], val))
- if self.show_impossible_implications:
- self.mark_impossible_implications(*coords)
- continue
-
- if retval:
- self.auto_fills += 1
-
- if self.grid.check_for_completeness():
- self.emit('puzzle-finished')
-
- return retval
-
- auto_fill = simple_debug(auto_fill)
-
- def auto_fill_current_entry(self):
- e = self.get_focused_entry()
- if not e:
- return None
- filled = self.grid.auto_fill_for_xy(e.x, e.y)
- if filled and filled != -1:
- e.set_text_interactive('')
- e.set_text_interactive(str(filled[1]))
-
-
- auto_fill_current_entry = simple_debug(auto_fill_current_entry)
-
- def mark_impossible_implications(self, x, y):
- if not self.grid:
- return None
- implications = self.grid.find_impossible_implications(x, y)
- if implications:
- for x, y in implications:
- self.__entries__[(x, y)].set_impossible(True)
- if (x, y) not in self.impossibilities:
- self.impossible_hints += 1
- continue
- self
-
-
- for x, y in self.impossibilities:
- if (x, y) not in implications:
- self.__entries__[(x, y)].set_impossible(False)
- continue
-
- self.impossibilities = implications
-
- mark_impossible_implications = simple_debug(mark_impossible_implications)
-
- def create_tracker(self, identifier = 0):
- if not identifier:
- identifier = 0
-
- while self.trackers.has_key(identifier):
- identifier += 1
- self.trackers[identifier] = []
- return identifier
-
- create_tracker = simple_debug(create_tracker)
-
- def trackers_for_point(self, x, y, val = None):
- if val:
- track_for_point = (None, None, filter)((lambda t: (x, y, val) in t[1]), self.trackers.items())
- else:
- track_for_point = (None, filter)((lambda tkr: [] in [ t[1] == y for t in tkr[1] ]), self.trackers.items())
- return [ t[0] for t in track_for_point ]
-
-
- def get_tracker_color(self, identifier):
- if len(TRACKER_COLORS) > identifier:
- return TRACKER_COLORS[identifier]
- random_color = TRACKER_COLORS[0]
- while random_color in TRACKER_COLORS:
- random_color = (random.randint(0, 100) / 100, random.randint(0, 100) / 100, random.randint(0, 100) / 100)
- continue
- len(TRACKER_COLORS) > identifier
- TRACKER_COLORS.append(random_color)
- return self.get_tracker_color(identifier)
-
-
- def toggle_tracker(self, identifier, value):
- '''Toggle tracking for tracker identified by identifier.'''
- self.__trackers_tracking__[identifier] = value
-
- toggle_tracker = simple_debug(toggle_tracker)
-
- def delete_by_tracker(self, identifier):
- '''Delete all cells tracked by tracker ID identifer.'''
- ret = []
- while self.trackers[identifier]:
- (x, y, v) = self.trackers[identifier][0]
- ret.append((x, y, v, self.trackers_for_point(x, y, v)))
- self.remove(x, y)
- if self.grid and self.grid._get_(x, y):
- self.grid.remove(x, y)
- continue
- return ret
-
-
- def delete_except_for_tracker(self, identifier):
- tracks = self.trackers[identifier]
- removed = []
- for x in range(self.group_size):
- for y in range(self.group_size):
- val = self.grid._get_(x, y)
- if val and (x, y, val) not in tracks and not self.grid.virgin._get_(x, y):
- removed.append((x, y, val, self.trackers_for_point(x, y, val)))
- self.remove(x, y)
- if self.grid and self.grid._get_(x, y):
- self.grid.remove(x, y)
-
- self.grid._get_(x, y)
-
-
- return removed
-
-
- def add_tracker(self, x, y, tracker, val = None):
- self.__entries__[(x, y)].set_color(self.get_tracker_color(tracker))
- if not val:
- val = self.grid._get_(x, y)
-
- self.trackers[tracker].append((x, y, val))
-
-
- def remove_tracker(self, x, y, tracker, val = None):
- if not val:
- val = self.grid._get_(x, y)
-
- self.trackers[tracker].remove((x, y, val))
-
-
-
- class GridDancer:
- DANCE_COLORS = [ colors.color_hex_to_float(hx) for hx in [
- '#cc0000',
- '#ef2929',
- '#f57900',
- '#fcaf3e',
- '#fce94f',
- '#8ae234',
- '#73d216',
- '#729fcf',
- '#3465a4',
- '#ad7fa8',
- '#75507b'] ]
- STEPS_PER_ANIMATION = 10
-
- def __init__(self, grid):
- self.animations = [
- self.value_dance,
- self.box_dance,
- self.col_dance,
- self.row_dance]
- self.current_animation = self.value_dance
- self.step = 0
- self.grid = grid
- self.dancing = False
- for box in self.grid.__entries__.values():
- if box.read_only == True:
- box.read_only = False
- box.read_only_hidden = True
-
- box.queue_draw()
-
-
-
- def start_dancing(self):
- for box in self.grid.__entries__.values():
- box.props.can_focus = False
-
- self.grid.get_toplevel().child_focus(gtk.DIR_TAB_BACKWARD)
- self.dancing = True
- gobject.timeout_add(350, self.dance_grid)
-
-
- def stop_dancing(self):
- self.dancing = False
- for box in self.grid.__entries__.values():
- box.props.can_focus = True
- if box.read_only_hidden == True:
- box.read_only = True
- box.read_only_hidden = False
- continue
-
- self.grid.unhighlight_cells()
-
-
- def do_dance_step(self):
- self.grid.__entries__[(random.randint(0, 8), random.randint(0, 8))].set_background_color(random.choice(self.DANCE_COLORS))
-
-
- def rotate_animation(self):
- ci = self.animations.index(self.current_animation)
- if ci + 1 == len(self.animations):
- ni = 0
- else:
- ni = ci + 1
- self.current_animation = self.animations[ni]
- self.step = 0
-
-
- def dance_grid(self):
- if not self.dancing:
- return None
- if self.step > self.STEPS_PER_ANIMATION:
- self.rotate_animation()
-
- if hasattr(self, 'adjustment'):
- self.adjustment += 1
- else:
- self.adjustment = 0
- if self.adjustment >= 9:
- self.adjustment = 0
-
-
- try:
- self.current_animation()
- except AttributeError:
- return True
-
- self.step += 1
- if self.dancing:
- return True
-
-
- def col_dance(self):
- for x in range(9):
- n = (x + self.adjustment) % len(self.DANCE_COLORS)
- color = self.DANCE_COLORS[n]
- for y in range(9):
- self.grid.__entries__[(x, y)].set_background_color(color)
-
-
-
-
- def row_dance(self):
- for y in range(9):
- n = (y + self.adjustment) % len(self.DANCE_COLORS)
- color = self.DANCE_COLORS[n]
- for x in range(9):
- self.grid.__entries__[(x, y)].set_background_color(color)
-
-
-
-
- def box_dance(self):
- for box in range(9):
- n = (box + self.adjustment) % len(self.DANCE_COLORS)
- color = self.DANCE_COLORS[n]
- for x, y in self.grid.grid.box_coords[box]:
- self.grid.__entries__[(x, y)].set_background_color(color)
-
-
-
-
- def value_dance(self):
- for value in range(10):
- n = (value + self.adjustment) % len(self.DANCE_COLORS)
- color = self.DANCE_COLORS[n]
- for x in range(9):
- for y in range(9):
- if self.grid.grid._get_(x, y) == value:
- self.grid.__entries__[(x, y)].set_background_color(color)
- continue
-
-
-
-
-
-
- def test_dance_grid(grid):
- dancer = GridDancer(grid)
- dancer.start_dancing()
-
- def stop(*args):
- dancer.stop_dancing()
-
- gobject.timeout_add(15000, stop)
-
-